//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#include "NjNinjaSync.h"
#include "NjAtomicStack.h"
#include "NjAssert.h"
#include "NjConfiguration.h"

//----------------------------------------------------------------------------
class NjAtomicStack::Static
{
public:
	static NjUint ToAddress(Pointer uSource);
	
private:
	NJ_DISABLE_INSTANCE(Static);
};
//----------------------------------------------------------------------------
void NjAtomicStack::PushRange(FlushNode * pNode)
{
	NJ_ASSERT(pNode!=NJ_NULL);

	Node * pBeforeEnd(reinterpret_cast<Node *>(pNode));

	for(;;)
	{
		Node * const pNext(pBeforeEnd->m_pNext);

		if (pNext==NJ_NULL)
			break;

		pBeforeEnd=pNext;
	}

	PushRange(pNode, reinterpret_cast<FlushNode *>(pBeforeEnd));
}
//----------------------------------------------------------------------------
void NjAtomicStack::PushRange(FlushNode * pNode, FlushNode * pBeforeEnd)
{
	NJ_ASSERT(pNode!=NJ_NULL && pBeforeEnd!=NJ_NULL);

	Node * pBeforeEndNode(reinterpret_cast<Node *>(pBeforeEnd));

	Pointer const uNode(reinterpret_cast<NjUint>(pNode));
	NJ_ASSERT(Static::ToAddress(uNode)==reinterpret_cast<NjUint>(pNode));

	for(;;)
	{
		Pointer const uPointer(m_kHeader.Get());
		pBeforeEndNode->m_pNext=reinterpret_cast<Node *>(NjUint(uPointer&ADDRESS_MASK));

		Pointer const uNewPointer(((uPointer+TAG_INCREMENT)&TAG_MASK)|uNode);

		if (m_kHeader.PostCompareExchange(uPointer, uNewPointer)==uPointer)
			break;
	}
}
//----------------------------------------------------------------------------
NjAtomicStack::Node * NjAtomicStack::Pop()
{
	Node * pResult;

	for(;;)
	{
		Pointer const uPointer(m_kHeader.Get());
		pResult=reinterpret_cast<Node *>(Static::ToAddress(uPointer));

		if (pResult==NJ_NULL)
			return NJ_NULL;

		Pointer const uNode(reinterpret_cast<NjUint>(pResult->m_pNext));
		NJ_ASSERT((uNode&ADDRESS_MASK)==uNode);

		Pointer const uNewPointer(((uPointer+TAG_INCREMENT)&TAG_MASK)|uNode);

		if (m_kHeader.PostCompareExchange(uPointer, uNewPointer)==uPointer)
			break;
	}

	return pResult;
}
//----------------------------------------------------------------------------
inline NjUint NjAtomicStack::Static::ToAddress(Pointer uSource)
{
#if NJ_PLATFORM_OS_LINUX && NJ_PLATFORM_64_BIT
	/// On linux both the upper and the lower part of the 64 bit address space is used
	/// See http://en.wikipedia.org/wiki/X86-64 for details
	static const Pointer ADDRESS_MASK_SIGN_BIT((ADDRESS_MASK+1)>>1);
	static const Pointer ADDRESS_NEGATIVE(~ADDRESS_MASK);
	
	NjUint const uAddress(uSource&ADDRESS_MASK);
	
	if ((uAddress&ADDRESS_MASK_SIGN_BIT)==0)
		return uAddress;
	
	return ADDRESS_NEGATIVE|uAddress;
#elif NJ_PLATFORM_OS_LINUX || NJ_PLATFORM_OS_WIN
	return NjUint(uSource&ADDRESS_MASK);
#else // NJ_PLATFORM_OS_LINUX || NJ_PLATFORM_OS_WIN
#	error "Unsupported build configuration"
#endif // NJ_PLATFORM_OS_LINUX || NJ_PLATFORM_OS_WIN
}
//----------------------------------------------------------------------------
